package com.miao.im.service.auth;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.SecureUtil;
import com.miao.im.common.CacheKey;
import com.miao.im.common.enums.LuaName;
import com.miao.im.domain.UserDo;
import com.miao.im.dto.AuthRequestDto;
import com.miao.im.dto.UserRespDto;
import com.miao.im.repository.IUserRepository;
import com.miao.im.repository.dao.IUserDao;
import com.miao.im.support.RedisLuaExecutor;
import com.miao.im.support.exception.AuthenticationException;
import com.miao.im.support.exception.BizException;
import com.miao.im.support.util.JsonUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * @Auther: miaoguoxin
 * @Date: 2021/4/11 20:23
 * @Description:
 */
@Service
@Primary
public class WebAuthManager implements IAuthManager<UserRespDto> {
    @Autowired
    private RedisLuaExecutor redisLuaExecutor;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private IUserDao userDao;
    @Autowired
    private IUserRepository userRepository;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserRespDto register(AuthRequestDto dto) {
        userDao.getByUsername(dto.getUsername()).ifPresent(userDo -> {
            throw new BizException("用户已注册");
        });
        UserDo webUser = UserDo.register(dto);
        userDao.insert(webUser);
        UserRespDto userRespDto = this.convert2Dto(webUser);
        this.exeSaveScript(userRespDto);
        return userRespDto;
    }

    @Override
    public UserRespDto login(AuthRequestDto dto) {
        UserDo userDo = userDao.getByUsername(dto.getUsername())
                .orElseThrow(() -> new BizException("用户不存在或密码错误"));
        String secondPwd = SecureUtil.md5(dto.getPassword() + userDo.getSalt());
        if (!secondPwd.equals(userDo.getPassword())) {
            throw new BizException("用户不存在或密码错误");
        }
        UserRespDto userRespDto = this.convert2Dto(userDo);
        this.exeSaveScript(userRespDto);
        return userRespDto;
    }

    @Override
    public Mono<UserRespDto> reactiveLogin(AuthRequestDto dto) {
        return Mono.empty();
    }

    @Override
    public void logout(String sessionId, String deviceNumber) {
        redisTemplate.delete(getKeys(sessionId, deviceNumber));
    }


    @Override
    public void allOut() {

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void refreshLoginTime(String sessionId, String deviceNumber) {
        UserDo userDo = userRepository.getBySessionId(sessionId)
                .orElseThrow(() -> new AuthenticationException("登录失效，请重新登录"));
        userRepository.updateUserInfo(userDo);
        this.exeRefreshScript(sessionId, deviceNumber);
    }


    private void exeSaveScript(UserRespDto userRespDto) {
        List<String> keys = getKeys(userRespDto.getSessionId(), userRespDto.getDeviceNumber());
        Object[] args = new Object[]{
                String.valueOf(CacheKey.WEB_LOGIN_SESSION_TIMEOUT_DAY * 24 * 60 * 60),
                JsonUtil.toJSONStr(userRespDto)
        };
        redisLuaExecutor.execute(LuaName.SAVE_SESSION, keys, args);
    }

    private void exeRefreshScript(String sessionId, String deviceNumber) {
        List<String> keys = getKeys(sessionId, deviceNumber);
        Object[] args = new Object[]{
                String.valueOf(CacheKey.WEB_LOGIN_SESSION_TIMEOUT_DAY * 24 * 60 * 60)
        };
        redisLuaExecutor.execute(LuaName.RENEW_SESSION, keys, args);
    }

    private UserRespDto convert2Dto(UserDo webUser) {
        UserRespDto respDto = new UserRespDto();
        respDto.setId(webUser.getId());
        respDto.setNickname(webUser.getNickname());
        respDto.setUsername(webUser.getUsername());
        respDto.setAvatar(webUser.getAvatar());
        respDto.setGender(webUser.getGender());
        respDto.setCreateTime(webUser.getCreateTime());
        respDto.setLastLoginTime(webUser.getLastLoginTime());
        respDto.setSessionId(IdUtil.simpleUUID());
        respDto.setDeviceNumber(webUser.getDeviceNumber());
        return respDto;
    }

    private List<String> getKeys(String sessionId, String deviceNumber) {
        return CollectionUtil.newArrayList(
                CacheKey.WEB_LOGIN_SESSION_PREFIX + sessionId,
                CacheKey.WEB_LOGIN_DEVICE_NUMBER_PREFIX + deviceNumber
        );
    }
}
